#include "login_module.h"

#include "WLcJSON.h"
#include "burst_module.h"
#include "corp_module.h"
#include "friend_module.h"
#include "group_module.h"
#include "wl_http_client.h"
#include "keepalive_module.h"
#include "location_module.h"
#include "protocol_client.h"
#include "repo.h"
#include "session_module.h"
#include "weilapb.h"
#include "weilapb_defines.h"
#include "wlsdk_define.h"

#ifdef __cplusplus
extern "C" {
#endif

#define DNS_CONTEXT_LOGIN_SERVER 1

#define HTTP_CLIENT_CONTEXT_GET_MSG_SERVER 1

typedef struct {
    wlsdk_message_callback callback;
} wl_login_global;

static wl_login_global s_login_global;

static void process_set_appkey(void* data, wl_int32 datalen);
static void process_set_hardware(void* data, wl_int32 datalen);
static void process_set_account(void* data, wl_int32 datalen);
static void process_set_login_url(void* data, wl_int32 datalen);
static void process_set_web_server(void* data, wl_int32 datalen);
static void process_login(void* data, wl_int32 datalen);
static void process_logout(void* data, wl_int32 datalen);
static void process_refresh_access_token(void* data, wl_int32 datalen);
static void process_answer_bind_extension(void* data, wl_int32 datalen);
static void process_login_message(PBWeilaMsg* message, void* content, wl_uint32 contentlen, void* context);
static void process_logout_message(PBWeilaMsg* message, void* content, wl_uint32 contentlen, void* context);
static void
    process_refresh_access_token_message(PBWeilaMsg* message, void* content, wl_uint32 contentlen, void* context);
static void
    process_answer_bind_extension_message(PBWeilaMsg* message, void* content, wl_uint32 contentlen, void* context);
static void process_bind_extension_notify_message(PBWeilaMsg* message, void* context);
static void
    process_answer_shutdown_extension_message(PBWeilaMsg* message, void* content, wl_uint32 contentlen, void* context);
static void process_shutdown_extension_notify_message(PBWeilaMsg* message, void* context);

static void login_request(void);
static void login_sdk_message_callback(wl_uint32 msgid, wl_int32 resultcode, void* msgdata, wl_uint32 datalen);

static void set_login_state(wl_uint32 state) {
    wl_uint32 current_state = repo_get_login_state();
    WLLOGD("state %d", state);
    switch (state) {
        // 离线
        case WLSDK_LOGIN_OFFLINE:
            if (WLSDK_LOGIN_LOGINING == current_state || WLSDK_LOGIN_SYNCING == current_state
                || WLSDK_LOGIN_ONLINE == current_state) {
                repo_set_login_state(state);
                login_sdk_message_callback(WLSDK_MSG_LOGIN_STATE, WLSDK_RESULT_SUCCESS, &state, sizeof(wl_uint32));
                wl_core_send_message(WL_MODULE_KEEPALIVE, KEEPALIVE_MSG_DISCONNECTED, wl_null, 0);
            }
            break;
        case WLSDK_LOGIN_LOGINING:
        case WLSDK_LOGIN_SYNCING:
            if (current_state != state) {
                repo_set_login_state(state);
                login_sdk_message_callback(WLSDK_MSG_LOGIN_STATE, WLSDK_RESULT_SUCCESS, &state, sizeof(wl_uint32));
            }
            break;
        case WLSDK_LOGIN_ONLINE:
            if (current_state != state) {
                repo_set_login_state(state);
                login_sdk_message_callback(WLSDK_MSG_LOGIN_STATE, WLSDK_RESULT_SUCCESS, &state, sizeof(wl_uint32));
                wl_core_send_message(WL_MODULE_KEEPALIVE, KEEPALIVE_MSG_CONNECTED, wl_null, 0);
            }
            break;
        case WLSDK_LOGIN_LOGOUT:
        case WLSDK_LOGIN_EXCEPTION:
        case WLSDK_LOGIN_KICKOUT:
            if (current_state != state) {
                repo_set_login_state(state);
                login_sdk_message_callback(WLSDK_MSG_LOGIN_STATE, WLSDK_RESULT_SUCCESS, &state, sizeof(wl_uint32));
                wl_core_send_message(WL_MODULE_KEEPALIVE, KEEPALIVE_MSG_DISCONNECTED, wl_null, 0);
            }
            break;
        default:
            break;
    }
}

static void set_extension_state(wl_uint32 state) {
    repo_set_extension_state(state);
    login_sdk_message_callback(WLSDK_MSG_LOGIN_EXTENSION_STATE, WLSDK_RESULT_SUCCESS, &state, sizeof(wl_uint32));
}

static void login_sdk_message_callback(wl_uint32 msgid, wl_int32 resultcode, void* msgdata, wl_uint32 datalen) {
    if (s_login_global.callback) {
        s_login_global.callback(msgid, resultcode, msgdata, datalen);
    }
}

static void login_server_dns_callback(wl_int32 resultcode, wl_char* host_name, wl_char* host, void* context) {
    login_server loginserver;
    repo_dns     dns;
    if (WL_DNS_PROCESSING == resultcode) {
        WLLOGW("dns %s processing", host_name);
        return;
    } else if (WL_DNS_SUCCESS == resultcode && strlen(host) > 0) {
        repo_add_dns(host_name, host);
        strncpy(dns.ip, host, sizeof(dns.ip) - 1);
    } else {
        if (0 == repo_get_dns(host_name, &dns)) {
            resultcode = WL_DNS_SUCCESS;
        }
    }
    WLLOGW("result %d host_name %s host %s ip %s stream state %d",
           resultcode,
           host_name,
           host,
           dns.ip,
           protocol_client_get_state());
    if (WL_DNS_SUCCESS == resultcode) {
        if (protocol_client_get_state() == PROTOCOL_CLIENT_STATE_DISCONNECTED) {
            strncpy(loginserver.host, dns.ip, sizeof(loginserver.host));
            loginserver.port = (wl_uint16)context;
            WLLOGW("login server host %s port %d", loginserver.host, loginserver.port);
            wl_core_send_message(WL_MODULE_LOGIN, LOGIN_MSG_OPEN_PROTOCOL_CLIENT, &loginserver, sizeof(login_server));
        }
    }
}

static wl_int32 login_message_callback(wl_int32 msgid, void* data, wl_uint32 datalen) {
    WLLOGD("msgid %d", msgid);
    switch (msgid) {
        case LOGIN_MSG_SET_APPKEY:
            process_set_appkey(data, datalen);
            break;
        case LOGIN_MSG_SET_HARDWARE:
            process_set_hardware(data, datalen);
            break;
        case LOGIN_MSG_SET_ACCOUNT:
            process_set_account(data, datalen);
            break;
        case LOGIN_MSG_SET_LOGIN_URL:
            process_set_login_url(data, datalen);
            break;
        case LOGIN_MSG_SET_WEB_SERVER:
            process_set_web_server(data, datalen);
            break;
        case LOGIN_MSG_GET_SERVER_STATE: {
            wl_uint32 state = protocol_client_get_state();
            login_sdk_message_callback(WLSDK_MSG_SYSTEM_SERVER_STATE, WLSDK_RESULT_SUCCESS, &state, sizeof(wl_uint32));
            break;
        }
        case LOGIN_MSG_LOGIN:
            process_login(data, datalen);
            break;
        case LOGIN_MSG_LOGOUT:
            process_logout(data, datalen);
            break;
        case LOGIN_MSG_GET_LOGIN_STATE: {
            wl_uint32 state = repo_get_login_state();
            login_sdk_message_callback(WLSDK_MSG_LOGIN_STATE, WLSDK_RESULT_SUCCESS, &state, sizeof(wl_uint32));
            break;
        }
        case LOGIN_MSG_GET_EXTENSION_STATE: {
            wl_uint32 state = repo_get_extension_state();
            login_sdk_message_callback(
                WLSDK_MSG_LOGIN_EXTENSION_STATE, WLSDK_RESULT_SUCCESS, &state, sizeof(wl_uint32));
            break;
        }
        case LOGIN_MSG_REFRESH_ACCESS_TOKEN: {
            process_refresh_access_token(data, datalen);
            break;
        }
        case LOGIN_MSG_ANSWER_BIND_EXTENSION:
            process_answer_bind_extension(data, datalen);
            break;
        case LOGIN_MSG_OPEN_PROTOCOL_CLIENT: {
            login_server* loginserver = (login_server*)data;
            if (0
                != protocol_client_connect_server(
                    loginserver->host, loginserver->port, PROTOCOL_CLIENT_REQUEST_TIMEOUT_15)) {
                WLLOGW("protocol_client_connect_server error");
                protocol_client_disconnect();
                login_sdk_message_callback(WLSDK_MSG_LOGIN_LOGIN, WLSDK_RESULT_LOCAL_CONNECT_FAIL, wl_null, 0);
            }
            break;
        }
        case LOGIN_MSG_CLOSE_PROTOCOL_CLIENT:
            protocol_client_disconnect();
            break;
        default:
            WLLOGW("msgid %d not support", msgid);
            break;
    }
    return 0;
}

static wl_int32 login_protocol_client_state_callback(wl_uint32 state) {
    // WLLOGW("state %d", state);
    switch (state) {
        case PROTOCOL_CLIENT_STATE_DISCONNECTED:
            set_login_state(WLSDK_LOGIN_OFFLINE);
            login_sdk_message_callback(WLSDK_MSG_SYSTEM_SERVER_STATE, WLSDK_RESULT_SUCCESS, &state, sizeof(wl_uint32));
            break;
        case PROTOCOL_CLIENT_STATE_CONNECTING:
            login_sdk_message_callback(WLSDK_MSG_SYSTEM_SERVER_STATE, WLSDK_RESULT_SUCCESS, &state, sizeof(wl_uint32));
            break;
        case PROTOCOL_CLIENT_STATE_CONNECTED:
            login_sdk_message_callback(WLSDK_MSG_SYSTEM_SERVER_STATE, WLSDK_RESULT_SUCCESS, &state, sizeof(wl_uint32));
            wl_core_send_message(WL_MODULE_LOGIN, LOGIN_MSG_LOGIN, wl_null, 0);
            break;
    }
    return 0;
}

static void login_http_client_callback(wl_uint32 resultcode,
                                       wl_char*  content,
                                       wl_uint32 content_len,
                                       wl_uint32 content_type,
                                       void*     context) {
    WLLOGW("result %d content: %s content_type %d", resultcode, content, content_type);
    if (WLSDK_RESULT_SUCCESS == resultcode) {
        if (content && HTTP_CLIENT_CONTENT_JSON == content_type) {
            WLcJSON* result_obj = WLcJSON_Parse(content);
            if (result_obj) {
                WLcJSON* errcode_obj = WLcJSON_GetObjectItem(result_obj, "errcode");
                if (errcode_obj) {
                    WLcJSON* data_obj = WLcJSON_GetObjectItem(result_obj, "data");
                    if (data_obj) {
                        WLcJSON* timestamp_obj = WLcJSON_GetObjectItem(data_obj, "timestamp");
                        if (timestamp_obj) {
                            wl_time timestamp = (wl_time)WLcJSON_GetNumberValue(timestamp_obj);
                            repo_set_server_timestamp(timestamp);
                            login_sdk_message_callback(WLSDK_MSG_SYSTEM_SERVER_TIMESTAMP,
                                                       WLSDK_RESULT_SUCCESS,
                                                       (void*)&timestamp,
                                                       sizeof(wl_time));
                        }else {
                            WLLOGW("timestamp not exist");
                        }
                        resultcode = (wl_int32)WLcJSON_GetNumberValue(errcode_obj);
                        WLLOGD("WLcJSON_GetNumberValue result %d", resultcode);
                        if (WLSDK_RESULT_SUCCESS == resultcode) {
                            WLcJSON* ip1_obj  = WLcJSON_GetObjectItem(data_obj, "ip1");
                            WLcJSON* host_obj = WLcJSON_GetObjectItem(data_obj, "host");
                            WLcJSON* port_obj = WLcJSON_GetObjectItem(data_obj, "port");
                            if ((ip1_obj || host_obj) && port_obj) {
                                login_server loginserver;
                                loginserver.port = (wl_uint16)WLcJSON_GetNumberValue(port_obj);
                                if (host_obj && WLcJSON_IsString(host_obj)) {
                                    strncpy(loginserver.host,
                                            (wl_char*)WLcJSON_GetStringValue(host_obj),
                                            sizeof(loginserver.host));
                                    WLLOGD("host %s port %d", loginserver.host, loginserver.port);
                                    // 域名解析
                                    if (0 > wl_dns_async_gethostbyname(loginserver.host,
                                                                       login_server_dns_callback,
                                                                       (void*)loginserver.port,
                                                                       WL_TASK_APP)) {
                                        resultcode = WLSDK_RESULT_DATA_INVALID;
                                    }
                                } else if (ip1_obj && WLcJSON_IsString(ip1_obj)) {
                                    strncpy(loginserver.host,
                                            (wl_char*)WLcJSON_GetStringValue(ip1_obj),
                                            sizeof(loginserver.host));
                                    
                                    WLLOGD("ip1 %s port %d", loginserver.host, loginserver.port);
                                    wl_core_send_message(WL_MODULE_LOGIN,
                                                         LOGIN_MSG_OPEN_PROTOCOL_CLIENT,
                                                         &loginserver,
                                                         sizeof(login_server));
                                }
                                WLcJSON_Delete(result_obj);
                                return;
                            } else {
                                WLLOGW("ip1 or host or port not exist");
                                resultcode = WLSDK_RESULT_DATA_INVALID;
                            }
                        }
                    } else {
                        WLLOGW("data not exist");
                        resultcode = WLSDK_RESULT_DATA_INVALID;
                    }

                } else {
                    WLLOGW("errcode not exist");
                    resultcode = WLSDK_RESULT_DATA_INVALID;
                }
                WLcJSON_Delete(result_obj);
            } else {
                WLLOGW("json parse error");
                resultcode = WLSDK_RESULT_DATA_INVALID;
            }
        }
    }
    if (WLSDK_RESULT_SUCCESS != resultcode) {
        login_sdk_message_callback(WLSDK_MSG_LOGIN_LOGIN, resultcode, wl_null, 0);
    }
}

static wl_int32
    login_protocol_client_request_callback(PBWeilaMsg* message, void* content, wl_uint32 contentlen, void* context) {
    WLLOGD("message %d resultCode %d", message->message, message->resultCode);
    switch (message->message) {
        case WL_LOGIN_LOGIN_MSG:
            process_login_message(message, content, contentlen, context);
            break;
        case WL_LOGIN_LOGOUT_MSG:
            process_logout_message(message, content, contentlen, context);
            break;
        case WL_LOGIN_REFRESH_ACCESS_TOKEN_MSG:
            process_refresh_access_token_message(message, content, contentlen, context);
            break;
        case WL_LOGIN_ANSWER_BIND_EXTENSION_MSG:
            process_answer_bind_extension_message(message, content, contentlen, context);
            break;
        case WL_LOGIN_ANSWER_SHUTDOWN_EXTENSION_MSG:
            process_answer_shutdown_extension_message(message, content, contentlen, context);
            break;
        default:
            break;
    }
    return 0;
}

static wl_int32 login_protocol_client_listen_callback(PBWeilaMsg* message, void* context) {
    WLLOGD("message %d", message->message);
    switch (message->message) {
        case WL_LOGIN_KICKOUT_MSG:
            set_login_state(WLSDK_LOGIN_KICKOUT);
            wl_core_send_message(WL_MODULE_LOGIN, LOGIN_MSG_CLOSE_PROTOCOL_CLIENT, wl_null, 0);
            break;
        case WL_LOGIN_BIND_EXTENSION_MSG:
            process_bind_extension_notify_message(message, context);
            break;
        case WL_LOGIN_UNBIND_EXTENSION_MSG: {
            // 清理数据
            wl_uint32 state = WLSDK_EXTENSION_NOBIND;
            repo_clear_account();
            set_extension_state(WLSDK_EXTENSION_NOBIND);
            login_sdk_message_callback(
                WLSDK_MSG_LOGIN_EXTENSION_STATE, WLSDK_RESULT_SUCCESS, &state, sizeof(wl_uint32));
            wl_core_send_message(WL_MODULE_BURST, BURST_MSG_CLEAR_ACCOUNT, wl_null, 0);
            wl_core_send_message(WL_MODULE_LOGIN, LOGIN_MSG_CLOSE_PROTOCOL_CLIENT, wl_null, 0);
            wl_core_send_message(WL_MODULE_LOGIN, LOGIN_MSG_LOGIN, wl_null, 0);
            break;
        }

        case WL_LOGIN_LOCK_EXTENSION:
        case WL_LOGIN_UNLOCK_EXTENSION: {
            wl_uint32* state = (wl_uint32*)message->pContent;
            set_extension_state(*state);
            break;
        }
        case WL_LOGIN_SHUTDOWN_EXTENSION_MSG: {
            process_shutdown_extension_notify_message(message, context);
            break;
        }
        case WL_LOGIN_SET_EXTENSION_CONFIG_MSG: {
            repo_set_extension_config((wl_char*)message->pContent);
            repo_refresh_default_session();
            wl_core_send_message(WL_MODULE_LOCATION, LOCATION_MSG_REFRESH_TRACK_CONFIG, wl_null, 0);
            break;
        }
        default:
            break;
    }
    return 0;
}

wl_int32 login_module_init(wlsdk_message_callback callback) {
    wl_int32  ret = 0;
    wl_module module;

    WLLOGI("login_module_init");
    ret = protocol_client_init(login_protocol_client_state_callback);
    if (0 != ret) {
        WLLOGW("protocol_client_init fail");
        return ret;
    }
    memset(&module, 0, sizeof(wl_module));
    module.moduleid = WL_MODULE_LOGIN;
    module.taskid   = WL_TASK_APP;
    module.callback = login_message_callback;
    ret             = wl_core_register_module(&module);
    if (0 != ret) {
        WLLOGW("wl_core_register_module login fail");
        protocol_client_end();
        return ret;
    }
    s_login_global.callback = callback;
    repo_set_login_state(WLSDK_LOGIN_OFFLINE);
    protocol_client_add_listen(WL_LOGIN_KICKOUT_MSG, WEILA_MSG_IND, login_protocol_client_listen_callback, wl_null);
    protocol_client_add_listen(
        WL_LOGIN_BIND_EXTENSION_MSG, WEILA_MSG_IND, login_protocol_client_listen_callback, wl_null);
    protocol_client_add_listen(
        WL_LOGIN_UNBIND_EXTENSION_MSG, WEILA_MSG_IND, login_protocol_client_listen_callback, wl_null);
    protocol_client_add_listen(WL_LOGIN_LOCK_EXTENSION, WEILA_MSG_IND, login_protocol_client_listen_callback, wl_null);
    protocol_client_add_listen(
        WL_LOGIN_UNLOCK_EXTENSION, WEILA_MSG_IND, login_protocol_client_listen_callback, wl_null);
    protocol_client_add_listen(
        WL_LOGIN_SET_EXTENSION_CONFIG_MSG, WEILA_MSG_IND, login_protocol_client_listen_callback, wl_null);

    WLLOGI("login_module_init success");
    return ret;
}

wl_int32 login_module_end(void) {
    WLLOGI("login_module_end");
    wl_core_unregister_module(WL_MODULE_LOGIN);
    protocol_client_end();
    s_login_global.callback = wl_null;
    return 0;
}

static void process_set_appkey(void* data, wl_int32 datalen) {
    login_appkey msgdata;
    repo_appkey  repodata;
    if (wl_null == data || sizeof(msgdata) != datalen) {
        WLLOGW("data is null or datalen not equal msgsize");
        return;
    }
    memcpy(&msgdata, data, datalen);
    strncpy(repodata.appid, msgdata.appid, sizeof(repodata.appid));
    repodata.appid[sizeof(repodata.appid) - 1] = '\0';
    strncpy(repodata.appkey, msgdata.appkey, sizeof(repodata.appkey));
    repodata.appkey[sizeof(repodata.appkey) - 1] = '\0';
    repo_set_appkey(&repodata);
}

static void process_set_hardware(void* data, wl_int32 datalen) {
    login_hardware msgdata;
    repo_hardware  repodata;
    if (wl_null == data || sizeof(msgdata) != datalen) {
        WLLOGW("data is null or datalen not equal msgsize");
        return;
    }
    memcpy(&msgdata, data, datalen);
    strncpy(repodata.id, msgdata.id, sizeof(repodata.id));
    repodata.id[sizeof(repodata.id) - 1] = '\0';
    strncpy(repodata.product, msgdata.product, sizeof(repodata.product));
    repodata.product[sizeof(repodata.product) - 1] = '\0';
    strncpy(repodata.imei, msgdata.imei, sizeof(repodata.imei));
    repodata.imei[sizeof(repodata.imei) - 1] = '\0';
    strncpy(repodata.imsi, msgdata.imsi, sizeof(repodata.imsi));
    repodata.imsi[sizeof(repodata.imsi) - 1] = '\0';
    strncpy(repodata.version, msgdata.version, sizeof(repodata.version));
    repodata.version[sizeof(repodata.version) - 1] = '\0';
    repodata.version_number                        = msgdata.version_number;
    strncpy(repodata.firmware, msgdata.firmware, sizeof(repodata.firmware));
    repodata.firmware[sizeof(repodata.firmware) - 1] = '\0';
    repodata.source_type                             = msgdata.source_type;
    repo_set_hardware(&repodata);
}

static void process_set_account(void* data, wl_int32 datalen) {
    login_account msgdata;
    repo_account  repodata;
    if (wl_null == data || sizeof(msgdata) != datalen) {
        WLLOGW("data is null or datalen not equal msgsize");
        return;
    }
    memcpy(&msgdata, data, datalen);
    strncpy(repodata.account, msgdata.account, sizeof(repodata.account));
    repodata.account[sizeof(repodata.account) - 1] = '\0';
    strncpy(repodata.country_code, msgdata.country_code, sizeof(repodata.country_code));
    repodata.country_code[sizeof(repodata.country_code) - 1] = '\0';
    strncpy(repodata.password, msgdata.password, sizeof(repodata.password));
    repodata.password[sizeof(repodata.password) - 1] = '\0';
    repodata.ismd5pwd                                = msgdata.ismd5pwd;
    repo_set_account(&repodata);
}

static void process_set_login_url(void* data, wl_int32 datalen) {
    login_url msgdata;
    repo_url  repodata;
    if (wl_null == data || sizeof(msgdata) != datalen) {
        WLLOGW("data is null or datalen not equal msgsize");
        return;
    }
    memcpy(&msgdata, data, datalen);
    strncpy(repodata.url, msgdata.url, sizeof(repodata.url));
    repodata.url[sizeof(repodata.url) - 1] = '\0';
    repo_set_login_url(&repodata);
}

static void process_set_web_server(void* data, wl_int32 datalen) {
    login_server msgdata;
    repo_server  repodata;
    if (wl_null == data || sizeof(msgdata) != datalen) {
        WLLOGW("data is null or datalen not equal msgsize");
        return;
    }
    memcpy(&msgdata, data, datalen);
    strncpy(repodata.host, msgdata.host, sizeof(repodata.host));
    repodata.host[sizeof(repodata.host) - 1] = '\0';
    repodata.port                            = msgdata.port;
    repo_set_web_server(&repodata);
}

static void process_login(void* data, wl_int32 datalen) {
    switch (protocol_client_get_state()) {
        case PROTOCOL_CLIENT_STATE_DISCONNECTED: {
            // 解析登录服务器域名
            repo_url      login_url;
            repo_hardware hardware;
            WLcJSON*      body_obj = wl_null;
            wl_char*      content  = wl_null;
            repo_get_hardware(&hardware);
            if (0 == strlen(hardware.imei) || 0 == strlen(hardware.product)) {
                WLLOGW("imei or product is not set");
                return;
            }
            repo_get_login_url(&login_url);
            body_obj = WLcJSON_CreateObject();
            if (wl_null == body_obj) {
                WLLOGW("create obj fail");
                return;
            }
            repo_build_http_sign(body_obj);
            content = WLcJSON_PrintUnformatted(body_obj);
            WLcJSON_Delete(body_obj);

            WLLOGW("login_url:%s \ncontent:%s", login_url.url, content);
            if (content) {
                if (0
                    != wl_http_client_post(login_url.url,
                                        HTTP_CLIENT_REQUEST_TIMEOUT_15,
                                        content,
                                        strlen(content),
                                        HTTP_CLIENT_CONTENT_JSON,
                                        login_http_client_callback,
                                        wl_null)) {
                    WLLOGW("wl_http_client_post %s error", login_url.url);
                    repo_set_login_state(WLSDK_LOGIN_OFFLINE);
                    protocol_client_disconnect();
                    login_sdk_message_callback(WLSDK_MSG_LOGIN_LOGIN, WLSDK_RESULT_LOCAL_HTTP_ERROR, wl_null, 0);
                    WLcJSON_free(content);
                    return;
                }
                WLcJSON_free(content);
                set_login_state(WLSDK_LOGIN_LOGINING);
            } else {
                repo_set_login_state(WLSDK_LOGIN_OFFLINE);
                login_sdk_message_callback(WLSDK_MSG_LOGIN_LOGIN, WLSDK_RESULT_LOCAL_OUT_OF_MEMORY, wl_null, 0);
                protocol_client_disconnect();
            }
            break;
        }
        case PROTOCOL_CLIENT_STATE_CONNECTING:
            break;
        case PROTOCOL_CLIENT_STATE_CONNECTED:
            if (WLSDK_LOGIN_SYNCING != repo_get_login_state() && WLSDK_LOGIN_ONLINE != repo_get_login_state()) {
                login_request();
            }
            break;
        default:
            break;
    }
}

static void process_logout(void* data, wl_int32 datalen) {
    PBEncResult enc_result = weilapb_buildLogoutReq();
    wl_int32    resultcode = protocol_client_send_request(
        &enc_result, wl_null, 0, PROTOCOL_CLIENT_REQUEST_TIMEOUT_15, login_protocol_client_request_callback, wl_null);
    if (0 > resultcode) {
        WLLOGW("protocol_client_send_request error");
        set_login_state(WLSDK_LOGIN_LOGOUT);
        protocol_client_disconnect();
        login_sdk_message_callback(WLSDK_MSG_LOGIN_LOGOUT, resultcode, wl_null, 0);
    }
}

static void process_refresh_access_token(void* data, wl_int32 datalen) {
    PBEncResult enc_result = weilapb_buildRefreshAccessTokenReq();
    wl_int32    resultcode = protocol_client_send_request(
        &enc_result, wl_null, 0, PROTOCOL_CLIENT_REQUEST_TIMEOUT_120, login_protocol_client_request_callback, wl_null);
    if (0 > resultcode) {
        WLLOGW("protocol_client_send_request error");
    }
}

static void process_answer_bind_extension(void* data, wl_int32 datalen) {
    wl_int32            resultcode = WLSDK_RESULT_FAILURE;
    wl_uint32           status     = *((wl_uint32*)data);
    repo_extension_bind extension_bind;
    PBEncResult         enc_result;
    repo_get_extension_bind(&extension_bind);

    enc_result = weilapb_buildAnswerBindExtensionReq(extension_bind.binderid, extension_bind.verify_code, status);
    resultcode = protocol_client_send_request(
        &enc_result, wl_null, 0, PROTOCOL_CLIENT_REQUEST_TIMEOUT_15, login_protocol_client_request_callback, wl_null);
    if (0 > resultcode) {
        WLLOGW("protocol_client_send_request error");
        login_sdk_message_callback(WLSDK_MSG_LOGIN_ANSWER_BIND_EXTENSION, resultcode, wl_null, 0);
    }
}

static void login_request(void) {
    wl_int32           resultcode = WLSDK_RESULT_FAILURE;
    repo_appkey        appkey;
    repo_account       account;
    repo_hardware      hardware;
    WLcJSON*           special_obj = wl_null;
    wl_char*           special     = wl_null;
    PBReqLoginExtParam param;
    PBEncResult        enc_result;

    repo_get_appkey(&appkey);
    repo_get_account(&account);
    repo_get_hardware(&hardware);

    special_obj = WLcJSON_CreateObject();
    WLcJSON_AddStringToObject(special_obj, "id", hardware.id);
    WLcJSON_AddStringToObject(special_obj, "product", hardware.product);
    WLcJSON_AddStringToObject(special_obj, "imei", hardware.imei);
    WLcJSON_AddStringToObject(special_obj, "imsi", hardware.imsi);
    WLcJSON_AddStringToObject(special_obj, "version", hardware.version);
    WLcJSON_AddStringToObject(special_obj, "sdk_version", WLSDK_VERSION);
    special = WLcJSON_PrintUnformatted(special_obj);
    WLcJSON_Delete(special_obj);
    weilapb_setDeviceConfig(appkey.appid, appkey.appkey, special, wl_null);

    memset(&param, 0, sizeof(PBReqLoginExtParam));
    param.baseParam.sizeOfStruct = sizeof(PBReqLoginExtParam);
    strncpy(param.appId, appkey.appid, sizeof(param.appId));
    param.appId[sizeof(param.appId) - 1] = '\0';
    strncpy(param.appKey, appkey.appkey, sizeof(param.appKey));
    param.appKey[sizeof(param.appKey) - 1] = '\0';
    /*strncpy(param.baseParam.account, account.account, sizeof(param.baseParam.account));
    param.baseParam.account[sizeof(param.baseParam.account) - 1] = '\0';
    strncpy(param.baseParam.password, account.password, sizeof(param.baseParam.password));
    param.baseParam.password[sizeof(param.baseParam.password) - 1] = '\0';*/
    strncpy(param.baseParam.countryCode, account.country_code, sizeof(param.baseParam.countryCode));
    param.baseParam.countryCode[sizeof(param.baseParam.countryCode) - 1] = '\0';
    param.baseParam.passwordWithMD5                                      = account.ismd5pwd;
    WLLOGD("account %s password %s country code %s ismd5pwd %d type %d",
           account.account,
           account.password,
           account.country_code,
           account.ismd5pwd,
           repo_get_extension_config_client_type());
    strncpy(param.deviceSpecial, special, sizeof(param.deviceSpecial));
    param.deviceSpecial[sizeof(param.deviceSpecial) - 1] = '\0';
    WLcJSON_free(special);

    ;
    param.baseParam.userConfigVersion = repo_get_extension_config_version();
    enc_result = weilapb_buildLoginReqWithType((PBReqLoginParam*)&param, repo_get_extension_config_client_type());
    resultcode = protocol_client_send_request(
        &enc_result, wl_null, 0, PROTOCOL_CLIENT_REQUEST_TIMEOUT_15, login_protocol_client_request_callback, wl_null);
    if (0 > resultcode) {
        WLLOGW("protocol_client_send_request error");
        protocol_client_disconnect();
        login_sdk_message_callback(WLSDK_MSG_LOGIN_LOGIN, resultcode, wl_null, 0);
    }
}

void process_login_message(PBWeilaMsg* message, void* content, wl_uint32 contentlen, void* context) {
    // 登录响应
    if (WLSDK_RESULT_SUCCESS == message->resultCode) {
        PBRspLogin* response = (PBRspLogin*)message->pContent;
        repo_user   user_info;
        wl_time     timestamp = response->timestamp;

        repo_set_token(response->token);
        repo_set_server_timestamp(response->timestamp);
        login_sdk_message_callback(
            WLSDK_MSG_SYSTEM_SERVER_TIMESTAMP, WLSDK_RESULT_SUCCESS, (void*)&timestamp, sizeof(wl_time));
        set_extension_state(response->extensionState);
        WLLOGW("extension state %d token %s userid %d number %s nick %s timestamp %lld",
               response->extensionState,
               response->token,
               response->userInfo.userId,
               response->userInfo.number,
               response->userInfo.nick,
               timestamp);
        set_login_state(WLSDK_LOGIN_ONLINE);
        login_sdk_message_callback(WLSDK_MSG_LOGIN_LOGIN, WLSDK_RESULT_SUCCESS, wl_null, 0);

        memset(&user_info, 0, sizeof(repo_user));
        user_info.user.userid = response->userInfo.userId;
        strncpy(user_info.user.number, response->userInfo.number, MAX_ACCOUNT_LEN);
        user_info.user.number[MAX_ACCOUNT_LEN - 1] = '\0';
        user_info.user.sex                         = response->userInfo.sex;
        strncpy(user_info.user.nick, response->userInfo.nick, MAX_NAME_LEN);
        user_info.user.nick[MAX_NAME_LEN - 1] = '\0';
        strncpy(user_info.user.phone, response->userInfo.phone, MAX_ACCOUNT_LEN);
        user_info.user.phone[MAX_ACCOUNT_LEN - 1] = '\0';
        strncpy(user_info.user.country_code, response->userInfo.countryCode, MAX_COUNTRYCODE_LEN);
        user_info.user.country_code[MAX_COUNTRYCODE_LEN - 1] = '\0';
        user_info.type                                       = response->userInfo.type;

        if (WLSDK_EXTENSION_BIND == response->extensionState || WLSDK_EXTENSION_LOCK == response->extensionState) {
            repo_hardware hardware;
            wl_uint32     client_type = repo_get_extension_config_client_type();
            WLLOGD("account %s password %s", response->extAccount, response->extPass);
            if (strlen(response->extAccount) && strlen(response->extPass)) {
                repo_account account;
                memset(&account, 0, sizeof(repo_account));
                strncpy(account.account, response->extAccount, MAX_ACCOUNT_LEN);
                account.account[MAX_ACCOUNT_LEN - 1] = '\0';
                strncpy(account.password, response->extPass, MAX_PASSWORD_LEN);
                account.password[MAX_PASSWORD_LEN - 1] = '\0';
                account.ismd5pwd                       = wl_true;
                account.country_code[0]                = '\0';
                repo_set_account(&account);
            }
            repo_set_selfinfo(&user_info);
            repo_set_user_config((wl_char*)response->userConfig);
            repo_set_extension_config((wl_char*)response->extensionConfig);
            repo_refresh_default_session();
            repo_get_hardware(&hardware);
            repo_set_managerid(response->extManagerId);
            login_sdk_message_callback(
                WLSDK_MSG_USER_CONFIG, WLSDK_RESULT_SUCCESS, response->userConfig, strlen(response->userConfig) + 1);
            wl_core_send_message(WL_MODULE_BURST, BURST_MSG_CHECK_SESSION, wl_null, 0);
            // client type change relogin
            if (repo_get_extension_config_client_type() != client_type) {
                wl_core_send_message(WL_MODULE_LOGIN, LOGIN_MSG_CLOSE_PROTOCOL_CLIENT, wl_null, 0);
                wl_core_send_message(WL_MODULE_LOGIN, LOGIN_MSG_LOGIN, wl_null, 0);
            }
            wl_core_send_message(WL_MODULE_LOCATION, LOCATION_MSG_REFRESH_TRACK_CONFIG, wl_null, 0);
            // 企业设备
            if ((0x10 & user_info.type) == 0x10) {
                // 同步企业信息
                wl_core_send_message(WL_MODULE_CORP, CORP_MSG_SYNC_START, wl_null, 0);
            }
            // 个人设备
            else {
                // 同步群版本列表
                wl_core_send_message(WL_MODULE_GROUP, GROUP_MSG_SYNC_GROUP_VERSION, wl_null, 0);
                // 同步好友
                wl_core_send_message(WL_MODULE_FRIEND, FRIEND_MSG_SYNC_FRIEND_INFO, wl_null, 0);
            }
        } else if (strlen(response->verificationCode)) {
            repo_extension_bind extension_bind;
            memset(&extension_bind, 0, sizeof(repo_extension_bind));
            strncpy(extension_bind.verify_code, response->verificationCode, sizeof(extension_bind.verify_code));
            extension_bind.verify_code[sizeof(extension_bind.verify_code) - 1] = '\0';
            repo_set_extension_bind(&extension_bind);
            WLLOGE("vercode %s", response->verificationCode);

            // 清理数据
            repo_clear_account();
            wl_core_send_message(WL_MODULE_BURST, BURST_MSG_CLEAR_ACCOUNT, wl_null, 0);
            login_sdk_message_callback(WLSDK_MSG_LOGIN_EXTENSION_VERCODE,
                                       WLSDK_RESULT_SUCCESS,
                                       response->verificationCode,
                                       strlen(response->verificationCode) + 1);
        }
    } else {
        switch (message->resultCode) {
            case WLSDK_RESULT_PARAM_INVALID:
            case WLSDK_RESULT_LOGIN_FORBID_IP:
            case WLSDK_RESULT_LOGIN_FORBID_USER:
            case WLSDK_RESULT_LOGIN_PASSWORD_ERROR_TOO_TIMES:
            case WLSDK_RESULT_LOGIN_ACCOUNT_OR_PASSWD_ERROR:
            case WLSDK_RESULT_LOGIN_EXTENSION_LAWLESSNESS:
            case WLSDK_RESULT_LOGIN_EXTENSION_LICENSE_LIMITED:
            case WLSDK_RESULT_LOGIN_EXTENSION_ACCOUNT_ERROR:
            case WLSDK_RESULT_LOGIN_EXTENSION_PASSWORD_ERROR:
            case WLSDK_RESULT_USER_NOT_EXIST:
                WLLOGD("WLSDK_LOGIN_EXCEPTION");
                set_login_state(WLSDK_LOGIN_EXCEPTION);
                break;
            default:
                break;
        }
        wl_core_send_message(WL_MODULE_LOGIN, LOGIN_MSG_CLOSE_PROTOCOL_CLIENT, wl_null, 0);
        login_sdk_message_callback(WLSDK_MSG_LOGIN_LOGIN, message->resultCode, wl_null, 0);
    }
}

void process_logout_message(PBWeilaMsg* message, void* content, wl_uint32 contentlen, void* context) {
    set_login_state(WLSDK_LOGIN_LOGOUT);
    wl_core_send_message(WL_MODULE_LOGIN, LOGIN_MSG_CLOSE_PROTOCOL_CLIENT, wl_null, 0);
    login_sdk_message_callback(WLSDK_MSG_LOGIN_LOGOUT, message->resultCode, wl_null, 0);
}

void process_refresh_access_token_message(PBWeilaMsg* message, void* content, wl_uint32 contentlen, void* context) {
    if (WLSDK_RESULT_SUCCESS == message->resultCode) {
        wl_char* accesstoken = (wl_char*)message->pContent;
        repo_set_token(accesstoken);
    }
    // 刷新失败重新登陆
    else {
        wl_core_send_message(WL_MODULE_LOGIN, LOGIN_MSG_CLOSE_PROTOCOL_CLIENT, wl_null, 0);
    }
}

void process_answer_bind_extension_message(PBWeilaMsg* message, void* content, wl_uint32 contentlen, void* context) {
    login_sdk_message_callback(WLSDK_MSG_LOGIN_ANSWER_BIND_EXTENSION, message->resultCode, wl_null, 0);
    if (WLSDK_RESULT_SUCCESS == message->resultCode) {
    }
    wl_core_send_message(WL_MODULE_LOGIN, LOGIN_MSG_CLOSE_PROTOCOL_CLIENT, wl_null, 0);
}

void process_answer_shutdown_extension_message(PBWeilaMsg* message,
                                               void*       content,
                                               wl_uint32   contentlen,
                                               void*       context) {
    login_sdk_message_callback(WLSDK_MSG_LOGIN_SHUTDOWN, message->resultCode, wl_null, 0);
    if (WLSDK_RESULT_SUCCESS == message->resultCode) {
    }
    wl_core_send_message(WL_MODULE_LOGIN, LOGIN_MSG_LOGOUT, wl_null, 0);
}

void process_bind_extension_notify_message(PBWeilaMsg* message, void* context) {
    PBNtfBindExtension*  notify = (PBNtfBindExtension*)message->pContent;
    repo_extension_bind  extension_bind;
    wlsdk_extension_bind bindinfo;
    repo_get_extension_bind(&extension_bind);
    extension_bind.binderid = notify->binderId;
    strncpy(extension_bind.binder_nick, notify->binderNick, sizeof(extension_bind.binder_nick));
    extension_bind.binder_nick[sizeof(extension_bind.binder_nick) - 1] = '\0';
    bindinfo.userid                                                    = notify->binderId;
    strncpy(bindinfo.nick, notify->binderNick, sizeof(bindinfo.nick));
    bindinfo.nick[sizeof(bindinfo.nick) - 1] = '\0';
    repo_set_extension_bind(&extension_bind);
    login_sdk_message_callback(
        WLSDK_MSG_LOGIN_BIND_EXTENSION, WLSDK_RESULT_SUCCESS, &bindinfo, sizeof(wlsdk_extension_bind));
}

void process_shutdown_extension_notify_message(PBWeilaMsg* message, void* context) {
    wl_uint32*  guardianId = (wl_uint32*)message->pContent;
    wl_int32    resultcode = WLSDK_RESULT_FAILURE;
    PBEncResult enc_result;

    enc_result = weilapb_buildAnswerShutdownExtensionReq(*guardianId);
    resultcode = protocol_client_send_request(
        &enc_result, wl_null, 0, PROTOCOL_CLIENT_REQUEST_TIMEOUT_15, login_protocol_client_request_callback, wl_null);
    if (0 > resultcode) {
        WLLOGW("protocol_client_send_request error");
    }
}

#ifdef __cplusplus
}
#endif
